﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using WorldBankBBS.Data;
using WorldBankBBS.Handlers;
using WorldBankBBS.Input;
using WorldBankBBS.Menus;
using WorldBankBBS.Pathing;
using WorldBankBBS.RichEditor;
using WorldBankBBS.Translators;
using Session = WorldBankBBS.Logic.Session;

namespace WorldBankBBS.EMail
{
    public class EMailHandler
    {
        private static event TextInputHandler TextInput;

        internal static List<PrivateMail> ListEmails(Session session, bool display = true)
        {
            var builder = new List<byte>();
            var translator = session.Encoding.GetTranslator(session);
            List<PrivateMail> list = null;
            var buffer = new byte[1024];
            var l = 0;
            if (display)
            {
                l = translator.StringToBytes(buffer, EMailStrings.EMAIL_LIST_TOP);
                for (int i = 0; i < l; ++i)
                    builder.Add(buffer[i]);
            }

            using (var mh = new ModelHelper())
            {
                mh.BeginNewContext();

                var count = 1;
                var unread = 0;
                list =
                    mh.Context.PrivateMails.Where(x => x.ToUserUID == session.User.UserUID && !x.IsDeleted)
                        .OrderBy(x => x.Timestamp)
                        .ToList();

                if (display)
                {
                    foreach (var email in list)
                    {
                        if (!email.IsRead) ++unread;
                        l = translator.StringToBytes(buffer, EMailStrings.EMAIL_LIST_ITEM, count++,
                            email.Timestamp.ToLocalTime().ToString("yyyy-MM-dd hh:mm tt"), email.Subject,
                            email.IsRead ? "" : "new");
                        for (int i = 0; i < l; ++i)
                            builder.Add(buffer[i]);
                    }

                    l = translator.StringToBytes(buffer, EMailStrings.EMAIL_LIST_BOT, count - 1, unread);
                    for (int i = 0; i < l; ++i)
                        builder.Add(buffer[i]);

                    var pages = Paginator.Paginate(builder.ToArray(), 0, builder.Count, 24, session.Encoding);

                    for (int p = 0; p < pages.Count; ++p)
                    {
                        session.Connection.SendData(pages[p]);
                        if (p != pages.Count - 1)
                            session.InvokePause(session.Encoding);
                    }
                }
            }

            return list;
        }

        public static void OnListEmails(object sender, Session session)
        {
            ListEmails(session, true);
        }

        public static void OnEnterEmailSection(object sender, Session session)
        {
            var menuProcessor = new MenuProcessor();
            var items = ListEmails(session, true);

            var context = new EMailContext() { Context = Guid.Empty.ToString() };
            session["MenuContext"] = context;

            var lookups = new Dictionary<string, Guid>();
            var count = 1;
            foreach (var l in items)
            {
                lookups.Add((count++).ToString(), l.PrivateMailUID);
            }
            session["MenuLookups"] = lookups;

            menuProcessor.ProcessMenu(new EmailMenuProvider(), session);

        }

        public static void OnPreCreateEmail(object sender, Session session)
        {
            var handler = new TextFileHandler(session);
            handler.SendFile(session, "TextFiles\\OnPreCreateEmail");
        }

        public static void OnCreateEmail(object sender, Session session)
        {
            InputHandler inputHandler = new InputHandler();

            var uid = (Guid)session["ReplyToUID"];
            session["ReplyToUID"] = Guid.Empty;
            var translator = session.Encoding.GetTranslator(session);
            var timestamp = DateTime.UtcNow;
            var sw = (int)session["ScreenWidth"];

            var buffer = new byte[1024];
            var l = 0;

            if (uid == Guid.Empty)
            {
                l = translator.StringToBytes(buffer, EMailStrings.EMAIL_TO_PROMPT);
                session.Connection.SendData(buffer, 0, l, l);

                TextInput -= inputHandler.OnTextInput;
                TextInput += inputHandler.OnTextInput;

                var to = TextInput(session, translator, session.Connection, 20, false);

                TextInput -= inputHandler.OnTextInput;

                if (to == "?")
                {
                    to = SelectFromUsersList(session);
                }

                var u = User.LoadByUsername(to);

                if (u != null)
                {
                    l = translator.StringToBytes(buffer, EMailStrings.EMAIL_SUBJECT_PROMPT);
                    session.Connection.SendData(buffer, 0, l, l);

                    TextInput -= inputHandler.OnTextInput;
                    TextInput += inputHandler.OnTextInput;

                    var subject = TextInput(session, translator, session.Connection, session.Encoding == "PETSCII" ? 30 : 70, false);

                    TextInput -= inputHandler.OnTextInput;

                    if (subject.Length > 0)
                    {
                        var te = new TextEditor();

                        if (te.Initialize(session,
                            new TextEditor.Field<string>(0, 0)
                            {
                                Format = "0:-" + (sw / 2 - 5),
                                LabelText = "{PURPLE}To{WHITE}:{LGREEN} ",
                                Value = (u.Alias ?? u.UserName)
                            },
                            new TextEditor.Field<DateTime>(sw / 2, 0)
                            {
                                Format = "0:g" + (sw / 2 - 8),
                                LabelText = "{PURPLE}Date{WHITE}:{LGREEN} ",
                                Value = timestamp.ToLocalTime()
                            },
                            new TextEditor.Field<string>(0, 1)
                            {
                                Format = "0",
                                LabelText = "",
                                Value = string.Empty
                            },
                            new TextEditor.Field<string>(0, 1)
                            {
                                Format = "0:-" + (sw - 1),
                                LabelText = "{PURPLE}Subject{WHITE}:{LGREEN} ",
                                Value = subject,
                            },
                            2, 1,
                            new TextEditor.Field<string>(0, (int)session["ScreenHeight"] - 1)
                            {
                                Format = "0:-" + (sw - 1),
                                LabelText = session.Encoding == "PETSCII" ? "{WHITE}. on blank line for command menu" : "{WHITE}^{YELLOW}A {WHITE}- {CYAN}Abort{WHITE}, ^{YELLOW}S {WHITE}- {CYAN}Save{WHITE}, ^{YELLOW}Z {WHITE}- {CYAN}Undo{WHITE}, ^{YELLOW}R {WHITE}- {CYAN}Redraw{WHITE}",
                                Value = string.Empty
                            }
                    ))
                        {
                            l = translator.StringToBytes(buffer, EMailStrings.EMAIL_POSTED);
                            session.Connection.SendData(buffer, 0, l, l);

                            using (var mh = new ModelHelper())
                            {
                                mh.BeginNewContext();

                                var msg = new PrivateMail
                                {
                                    FromUserUID = session.User.UserUID,
                                    ToUserUID = u.UserUID,
                                    PrivateMailUID = Guid.NewGuid(),
                                    Timestamp = DateTime.UtcNow,
                                    Body = te.Text,
                                    Subject = subject
                                };

                                mh.Context.PrivateMails.Add(msg);

                                var notification = new Notification
                                {
                                    NotificationUID = Guid.NewGuid(),
                                    NotificationText =
                                        string.Format("{0} sent you an email.", ((User)session.User).Alias),
                                    Timestamp = msg.Timestamp,
                                    NotificationPath =
                                        string.Format(
                                            "WorldBankBBS.EMail:WorldBankBBS.EMail.EMailPathHandler:ReadEMail|{0}",
                                            msg.PrivateMailUID),
                                    UserUID = u.UserUID,
                                };

                                mh.Context.Notifications.Add(notification);

                                mh.Context.SaveChanges();
                            }
                        }
                        else
                        {
                            l = translator.StringToBytes(buffer, EMailStrings.EMAIL_ABORTED);
                            session.Connection.SendData(buffer, 0, l, l);

                        }
                    }
                }
            }
            else
            {
                var te = new TextEditor();

                using (var mh = new ModelHelper())
                {
                    mh.BeginNewContext();
                    var replyTo = mh.Context.PrivateMails.FirstOrDefault(r => r.PrivateMailUID == uid);

                    if (te.Initialize(session,
                            new TextEditor.Field<string>(0, 0)
                            {
                                Format = "0:-" + (sw / 2 - 5),
                                LabelText = "{PURPLE}To{WHITE}:{LGREEN} ",
                                Value = (replyTo.User.Alias ?? replyTo.User.UserName)
                            },
                            new TextEditor.Field<DateTime>(sw / 2, 0)
                            {
                                Format = "0:g",
                                LabelText = "{LGREEN}",
                                Value = timestamp.ToLocalTime()
                            },
                            null,
                            new TextEditor.Field<string>(0, 1)
                            {
                                Format = "0:-" + (sw - 1),
                                LabelText = "{PURPLE}Subject{WHITE}:{LGREEN} ",
                                Value = "RE: " + (!replyTo.Subject.StartsWith("RE: ") ? "RE: " + replyTo.Subject : replyTo.Subject),
                            },
                            2, 1,
                            new TextEditor.Field<string>(0, (int)session["ScreenHeight"] - 1)
                            {
                                Format = "0:-" + (sw - 1),
                                LabelText = session.Encoding == "PETSCII" ? "{WHITE}. on blank line for command menu" : "{WHITE}^{YELLOW}A {WHITE}- {CYAN}Abort{WHITE}, ^{YELLOW}S {WHITE}- {CYAN}Save{WHITE}, ^{YELLOW}Z {WHITE}- {CYAN}Undo{WHITE}, ^{YELLOW}R {WHITE}- {CYAN}Redraw{WHITE}",
                                Value = string.Empty,
                            }
                    ))
                    {
                        l = translator.StringToBytes(buffer, EMailStrings.EMAIL_POSTED);
                        session.Connection.SendData(buffer, 0, l, l);

                        var msg = new PrivateMail
                        {
                            FromUserUID = session.User.UserUID,
                            ToUserUID = replyTo.FromUserUID,
                            PrivateMailUID = Guid.NewGuid(),
                            Timestamp = DateTime.UtcNow,
                            Body = te.Text,
                            Subject = replyTo.Subject
                        };

                        mh.Context.PrivateMails.Add(msg);

                        var notification = new Notification
                        {
                            NotificationUID = Guid.NewGuid(),
                            NotificationText =
                                string.Format("{0} sent you an email.", ((User)session.User).Alias),
                            Timestamp = msg.Timestamp,
                            NotificationPath =
                                string.Format(
                                    "WorldBankBBS.EMail:WorldBankBBS.EMail.EMailPathHandler:ReadEMail|{0}",
                                    msg.PrivateMailUID),
                            UserUID = replyTo.FromUserUID,
                        };

                        mh.Context.Notifications.Add(notification);

                        mh.Context.SaveChanges();
                    }
                    else
                    {
                        l = translator.StringToBytes(buffer, EMailStrings.EMAIL_ABORTED);
                        session.Connection.SendData(buffer, 0, l, l);

                    }
                }

            }
        }

        private static string SelectFromUsersList(Session session)
        {
            var translator = session.Encoding.GetTranslator(session);
            var buffer = new byte[1024];
            var inputHandler = new InputHandler();
            var l = 0;
            var to = string.Empty;

            using (var mh = new ModelHelper())
            {
                mh.BeginNewContext();

                l = translator.StringToBytes(buffer, EMailStrings.EMAIL_TO_LIST_HEADER);
                session.Connection.SendData(buffer, 0, l, 1);

                var list = mh.Context.Users.OrderBy(x => x.Alias).ToList();

                for (int counter = 0; counter < list.Count; ++counter)
                {
                    l = translator.StringToBytes(buffer, EMailStrings.EMAIL_TO_LIST_ITEM, counter, list[counter].Alias);
                    session.Connection.SendData(buffer, 0, l, 1);

                    if ((counter + 1) % 20 == 0 && counter + 1 < list.Count)
                    {

                        while (to == string.Empty)
                        {
                            l = translator.StringToBytes(buffer, EMailStrings.EMAIL_TO_LIST_MORE_ITEMS);
                            session.Connection.SendData(buffer, 0, l, l);

                            TextInput -= inputHandler.OnTextInput;
                            TextInput += inputHandler.OnTextInput;

                            to = TextInput(session, translator, session.Connection, 5, false);

                            TextInput -= inputHandler.OnTextInput;

                            if (to == string.Empty) break;

                            var index = -1;

                            if (Int32.TryParse(to, out index) && index >= 0 && index < list.Count)
                            {
                                var user = list[index];
                                to = user.UserName;
                                break;
                            }
                        }

                        if (to != string.Empty) break;
                    }
                }

                while (to == string.Empty)
                {
                    l = translator.StringToBytes(buffer, EMailStrings.EMAIL_TO_LIST_NO_MORE_ITEMS);
                    session.Connection.SendData(buffer, 0, l, l);

                    TextInput -= inputHandler.OnTextInput;
                    TextInput += inputHandler.OnTextInput;

                    to = TextInput(session, translator, session.Connection, 5, false);

                    TextInput -= inputHandler.OnTextInput;

                    if (to == string.Empty) break;

                    var index = -1;

                    if (Int32.TryParse(to, out index) && index >= 0 && index < list.Count)
                    {
                        var user = list[index];
                        to = user.UserName;
                        break;
                    }
                }
            }

            if (to != string.Empty)
            {
                l = translator.StringToBytes(buffer, EMailStrings.EMAIL_SENDING_TO, to);
                session.Connection.SendData(buffer, 0, l, l);
            }

            return to;
        }

        public static void OnReadEmail(object sender, Session session)
        {
            var uid = (Guid)session["PrivateMailUID"];

            using (var mh = new ModelHelper())
            {
                mh.BeginNewContext();

                var email = mh.Context.PrivateMails.FirstOrDefault(m => m.PrivateMailUID == uid);

                if (email != null && !email.IsDeleted)
                {
                    session["MenuContext"] = new EMailContext() { Context = uid.ToString() };

                    var translator = session.Encoding.GetTranslator(session);

                    var buffer = new byte[1024];
                    var l = translator.StringToBytes(buffer, EMailStrings.EMAIL_HEADER_TOP);
                    session.Connection.SendData(buffer, 0, l, l);

                    l = translator.StringToBytes(buffer, EMailStrings.EMAIL_HEADER_FROM, email.User.Alias);
                    session.Connection.SendData(buffer, 0, l, l);

                    l = translator.StringToBytes(buffer, EMailStrings.EMAIL_HEADER_DATE,
                        email.Timestamp.ToLocalTime().ToString("yyyy-MM-dd hh:mm tt"));
                    session.Connection.SendData(buffer, 0, l, l);

                    l = translator.StringToBytes(buffer, EMailStrings.EMAIL_HEADER_SUBJECT, email.Subject);
                    session.Connection.SendData(buffer, 0, l, l);

                    l = translator.StringToBytes(buffer, EMailStrings.EMAIL_HEADER_BOTTOM);
                    session.Connection.SendData(buffer, 0, l, l);

                    l = translator.StringToBytes(buffer, "{CRLF}{LGRAY}{{0}", email.Body);
                    var bb = buffer.WrapText(0, l, session.Encoding, (int)session["ScreenWidth"]);
                    var pages = bb.Paginate(0, bb.Length, 20, session.Encoding);
                    for (int b = 0; b < pages.Count; ++b)
                    {
                        session.Connection.SendData(pages[b], 0, pages[b].Length, 1);
                        if (b != pages.Count - 1)
                            session.InvokePause(session.Encoding);
                    }

                    email.IsRead = true;
                    mh.Context.SaveChanges();
                }
                else
                {
                    var translator = session.Encoding.GetTranslator(session);
                    var buffer = new byte[1024];
                    var l = translator.StringToBytes(buffer, EMailStrings.EMAIL_NOT_FOUND);
                    session.Connection.SendData(buffer, 0, l, l);
                }
            }
        }

        public static void OnPreDeleteEmail(object sender, Session session)
        {
            var translator = session.Encoding.GetTranslator(session);

            var buffer = new byte[1024];
            var l = 0;

            l = translator.StringToBytes(buffer, EMailStrings.EMAIL_PRE_DELETE);
            session.Connection.SendData(buffer, 0, l, l);
        }

        public static void OnPostDeleteEmail(object sender, Session session)
        {
            var translator = session.Encoding.GetTranslator(session);

            var buffer = new byte[1024];
            var l = 0;

            l = translator.StringToBytes(buffer, EMailStrings.EMAIL_POST_DELETE);
            session.Connection.SendData(buffer, 0, l, l);
        }

        public static void OnDeleteEmail(object sender, Session session)
        {
            var uid = (Guid)session["PrivateMailUID"];

            using (var mh = new ModelHelper())
            {
                mh.BeginNewContext();

                var email = mh.Context.PrivateMails.FirstOrDefault(m => m.PrivateMailUID == uid);

                if (email != null)
                {
                    email.IsDeleted = true;

                    mh.Context.SaveChanges();
                }
            }
        }
    }
}
